bitkeeper revision 1.370 (3f1bf42fuDsd9ULrhDoxWR8V_vV2zQ)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 21 Jul 2003 14:09:51 +0000 (14:09 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 21 Jul 2003 14:09:51 +0000 (14:09 +0000)
time.c, hypervisor-if.h, domain.c:
  Fixed more time code in Xen and Xenolinux. Reduced the chance of guest OS time going backwards. Better fixed-point arithmetic when calculating current system time.

xen/arch/i386/time.c
xen/common/domain.c
xen/include/hypervisor-ifs/hypervisor-if.h
xenolinux-2.4.21-sparse/arch/xeno/kernel/time.c

index 15b5d333f9aba17a6a59a4b53f0296ecdefec798..b0159250c234633facb4aa9df3354c2d763a54cd 100644 (file)
  ****************************************************************************
  */
 
-/*
- * Note from KAF: We should probably be more careful about overflow
- * of our timestamp cycle counter value, as we only use 31 bits of
- * precision. This will overflow on a 3GHz processor in less than a second,
- * and the situation is only going to get worse. 
- * 
- * Probably we should use bits N-N+31 of the TSC rather than 0-31, and
- * adjust scale_f and scale_i accordingly. If we're really smart we'd
- * calculate N dynamically, according to the measured CPU speed!
- * 
- * I think the current code limps along okay for now though.
- */
-
 /*
  *  linux/arch/i386/kernel/time.c
  *
@@ -65,6 +52,9 @@
 unsigned long cpu_khz;  /* Detected as we calibrate the TSC */
 unsigned long ticks_per_usec; /* TSC ticks per microsecond. */
 
+/* We use this to prevent overflow of 31-bit RDTSC "diffs". */
+static unsigned int rdtsc_bitshift;
+
 spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 
 int timer_ack=0;
@@ -288,26 +278,18 @@ s_time_t    stime_now;   /* time in ns at last timer IRQ */
 static inline s_time_t __get_s_time(void)
 {
     s32      delta_tsc;
-    u32      low, pcc;
-    u64      delta;
-    s_time_t now;
-
-    pcc = stime_pcc;        
-    now = stime_now;
-
-    /*
-     * We only use the bottom 32 bits of the TSC. This should be sufficient,
-     * although we take care that TSC on this CPU may be lagging the master TSC
-     * slightly. In this case we clamp the TSC difference to a minimum of zero.
-     */
-    rdtscl(low);
-    delta_tsc = low - pcc;
+    u32      low;
+    u64      delta, tsc;
+    
+    rdtscll(tsc);
+    low = (u32)(tsc >> rdtsc_bitshift);
+    delta_tsc = (s32)(low - stime_pcc);
     if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
     delta = ((u64)delta_tsc * st_scale_f);
     delta >>= 32;
     delta += ((u64)delta_tsc * st_scale_i);
 
-    return now + delta;
+    return stime_now + delta;
 }
 
 s_time_t get_s_time(void)
@@ -359,11 +341,13 @@ void update_dom_time(shared_info_t *si)
     unsigned long flags;
 
     spin_lock_irqsave(&stime_lock, flags);
-    si->system_time  = stime_now;
-    si->st_timestamp = stime_pcc;
-    si->tv_sec       = wall_clock_time.tv_sec;
-    si->tv_usec      = wall_clock_time.tv_usec;
-    si->wc_timestamp = wctime_st;
+    si->cpu_freq       = cpu_freq;
+    si->rdtsc_bitshift = rdtsc_bitshift;
+    si->system_time    = stime_now;
+    si->st_timestamp   = stime_pcc;
+    si->tv_sec         = wall_clock_time.tv_sec;
+    si->tv_usec        = wall_clock_time.tv_usec;
+    si->wc_timestamp   = wctime_st;
     si->wc_version++;
     spin_unlock_irqrestore(&stime_lock, flags);
 
@@ -420,7 +404,7 @@ static void update_scale(void)
     cpu_freq = cpu_freqs[freq_index];
 
     /* adjust scaling factor */
-    scale = 1000000000LL << 32;
+    scale = 1000000000LL << (32 + rdtsc_bitshift);
     scale /= cpu_freq;
     st_scale_f = scale & 0xffffffff;
     st_scale_i = scale >> 32;
@@ -438,21 +422,15 @@ static void update_time(unsigned long foo)
     unsigned long  flags;
     s_time_t       new_st;
     unsigned long  usec;
-    static int calls_since_scale_update = 0;
+    u64            full_pcc;
+    static int     calls_since_scale_update = 0;
 
     spin_lock_irqsave(&stime_lock, flags);
 
     /* Update system time. */
     stime_now = new_st = __get_s_time();
-    rdtscl(stime_pcc);
-
-    /* Maybe update our rate to be in sync with the RTC. */
-    if ( ++calls_since_scale_update >= 
-         (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) )
-    {
-        update_scale();
-        calls_since_scale_update = 0;
-    }
+    rdtscll(full_pcc);
+    stime_pcc = (u32)(full_pcc >> rdtsc_bitshift);
 
     /* Update wall clock time. */
     usec = ((unsigned long)(new_st - wctime_st))/1000;
@@ -464,6 +442,14 @@ static void update_time(unsigned long foo)
     wall_clock_time.tv_usec = usec;
     wctime_st = new_st;
 
+    /* Maybe update our rate to be in sync with the RTC. */
+    if ( ++calls_since_scale_update >= 
+         (SCALE_UPDATE_PERIOD/TIME_UPDATE_PERIOD) )
+    {
+        update_scale();
+        calls_since_scale_update = 0;
+    }
+
     spin_unlock_irqrestore(&stime_lock, flags);
 
     TRC(printk("TIME[%02d] update time: stime_now=%lld now=%lld,wct=%ld:%ld\n",
@@ -486,25 +472,31 @@ int __init init_xeno_time()
     u32      cpu_cycle;  /* time of one cpu cyle in pico-seconds */
     u64      scale;      /* scale factor */
     s64      freq_off;
+    u64      full_pcc;
+    unsigned int cpu_ghz;
 
     spin_lock_init(&stime_lock);
 
-    printk("Init Time[%02d]:\n", cpu);
+    cpu_ghz = (unsigned int)(cpu_freq / 1000000000ULL);
+    for ( rdtsc_bitshift = 0; cpu_ghz != 0; rdtsc_bitshift++, cpu_ghz >>= 1 )
+        continue;
+
+    printk("Init Time[%02d]: %u\n", cpu, rdtsc_bitshift);
 
     /* System Time */
     cpu_cycle   = (u32) (1000000000LL/cpu_khz); /* in pico seconds */
 
-    scale = 1000000000LL << 32;
-    scale /= cpu_freq;
-    st_scale_f = scale & 0xffffffff;
-    st_scale_i = scale >> 32;
-
     /* calculate adjusted frequencies */
     freq_off  = cpu_freq/1000; /* .1%  */
     cpu_freqs[0] = cpu_freq + freq_off;
     cpu_freqs[1] = cpu_freq;
     cpu_freqs[2] = cpu_freq - freq_off;
 
+    scale = 1000000000LL << (32 + rdtsc_bitshift);
+    scale /= cpu_freq;
+    st_scale_f = scale & 0xffffffff;
+    st_scale_i = scale >> 32;
+
     /* Wall Clock time */
     wall_clock_time.tv_sec  = get_cmos_time();
     wall_clock_time.tv_usec = 0;
@@ -514,7 +506,8 @@ int __init init_xeno_time()
 
     /* set starting times */
     stime_now = (s_time_t)0;
-    rdtscl(stime_pcc);
+    rdtscll(full_pcc);
+    stime_pcc = (u32)(full_pcc >> rdtsc_bitshift);
     wctime_st = NOW();
 
     /* start timer to update time periodically */
index 1a36ca9750e58d69eb99cd585e4fdaa8131cc403..de004b0d9deac3796719ef3f17ebbc6c169cb804 100644 (file)
@@ -382,7 +382,6 @@ int final_setup_guestos(struct task_struct * p, dom_meminfo_t * meminfo)
 
     /* set up the shared info structure */
     update_dom_time(p->shared_info);
-    p->shared_info->cpu_freq     = cpu_freq;
     p->shared_info->domain_time  = 0;
 
     /* we pass start info struct to guest os as function parameter on stack */
@@ -635,7 +634,6 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params,
 
     /* Set up shared info area. */
     update_dom_time(p->shared_info);
-    p->shared_info->cpu_freq     = cpu_freq;
     p->shared_info->domain_time  = 0;
 
     virt_startinfo_address = (start_info_t *)
index 0dfd07cb6011f83120d8963737a893cfad3f92ec..fd91a7d57986ff79994af6372b064b21b80f2733 100644 (file)
@@ -193,20 +193,21 @@ typedef struct shared_info_st {
     /*
      * Time: The following abstractions are exposed: System Time, Clock Time,
      * Domain Virtual Time. Domains can access Cycle counter time directly.
-     * XXX RN: Need something to pass NTP scaling to GuestOS.
+     * 
+     * The following values are updated periodically (and atomically, from the
+     * p.o.v. of the guest OS). Th eguest OS detects this because the wc_version
+     * is incremented.
      */
-
-    u64                  cpu_freq;         /* to calculate ticks -> real time */
-
+    u32                       wc_version;      /* a version number for info below */
+    unsigned int       rdtsc_bitshift;  /* use bits N:N+31 of TSC          */
+    u64                       cpu_freq;        /* to calculate ticks -> real time */
     /* System Time */
-    long long         system_time;     /* in ns */
-    unsigned long      st_timestamp;   /* cyclecounter at last update */
-
+    long long         system_time;     /* in ns */
+    unsigned long      st_timestamp;    /* cyclecounter at last update */
     /* Wall Clock Time */
-    u32                       wc_version;      /* a version number for info below */
-    long              tv_sec;          /* essentially a struct timeval */
+    long              tv_sec;          /* essentially a struct timeval */
     long              tv_usec;
-    long long         wc_timestamp;    /* system time at last update */
+    long long         wc_timestamp;    /* system time at last update */
     
     /* Domain Virtual Time */
     unsigned long long domain_time;
index 55b100e2573556b8037c095da97018c50b08dbbe..e02f726757332fd1c702b362a787723bf29b2ff9 100644 (file)
@@ -76,9 +76,9 @@ spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 extern rwlock_t xtime_lock;
 
 unsigned long cpu_khz; /* get this from Xen, used elsewhere */
-static spinlock_t hyp_stime_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t hyp_wctime_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t hyp_time_lock = SPIN_LOCK_UNLOCKED;
 
+static unsigned int rdtsc_bitshift;
 static u32 st_scale_f;
 static u32 st_scale_i;
 static u32 shadow_st_pcc;
@@ -92,37 +92,22 @@ static s64 shadow_st;
  * and use the cycle counter value as the "version" number. Clashes
  * should be very rare.
  */
-static inline long long get_s_time(void)
+static inline s64 __get_s_time(void)
 {
-       unsigned long flags;
-    u32           delta_tsc, low, pcc;
-       u64           delta;
-       s64           now;
-
-       spin_lock_irqsave(&hyp_stime_lock, flags);
-
-       while ((pcc = HYPERVISOR_shared_info->st_timestamp) != shadow_st_pcc)
-       {
-               barrier();
-               shadow_st_pcc = pcc;
-               shadow_st     = HYPERVISOR_shared_info->system_time;
-               barrier();
-       }
-
-    now = shadow_st;
-    /* only use bottom 32bits of TSC. This should be sufficient */
-       rdtscl(low);
-    delta_tsc = low - pcc;
+    s32 delta_tsc;
+    u32 low;
+       u64 delta, tsc;
+
+    rdtscll(tsc);
+    low = (u32)(tsc >> rdtsc_bitshift);
+    delta_tsc = (s32)(low - shadow_st_pcc);
+    if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
        delta = ((u64)delta_tsc * st_scale_f);
        delta >>= 32;
        delta += ((u64)delta_tsc * st_scale_i);
 
-       spin_unlock_irqrestore(&hyp_time_lock, flags);
-
-    return now + delta; 
-
+    return shadow_st + delta;
 }
-#define NOW()                          ((long long)get_s_time())
 
 /*
  * Wallclock time.
@@ -139,21 +124,35 @@ void do_gettimeofday(struct timeval *tv)
        unsigned long flags;
     long          usec, sec;
        u32               version;
-       u64           now;
+       u64           now, cpu_freq, scale;
 
-       spin_lock_irqsave(&hyp_wctime_lock, flags);
+       spin_lock_irqsave(&hyp_time_lock, flags);
 
-       while ((version = HYPERVISOR_shared_info->wc_version)!= shadow_wc_version)
+       while ( (version = HYPERVISOR_shared_info->wc_version) != 
+            shadow_wc_version )
        {
                barrier();
+
                shadow_wc_version   = version;
                shadow_tv_sec       = HYPERVISOR_shared_info->tv_sec;
                shadow_tv_usec      = HYPERVISOR_shared_info->tv_usec;
                shadow_wc_timestamp = HYPERVISOR_shared_info->wc_timestamp;
+               shadow_st_pcc       = HYPERVISOR_shared_info->st_timestamp;
+               shadow_st           = HYPERVISOR_shared_info->system_time;
+
+        rdtsc_bitshift      = HYPERVISOR_shared_info->rdtsc_bitshift;
+        cpu_freq            = HYPERVISOR_shared_info->cpu_freq;
+
+        /* XXX cpu_freq as u32 limits it to 4.29 GHz. Get a better do_div! */
+        scale = 1000000000LL << (32 + rdtsc_bitshift);
+        do_div(scale,(u32)cpu_freq);
+        st_scale_f = scale & 0xffffffff;
+        st_scale_i = scale >> 32;
+
                barrier();
        }
 
-       now   = NOW();
+       now   = __get_s_time();
        usec  = ((unsigned long)(now-shadow_wc_timestamp))/1000;
        sec   = shadow_tv_sec;
        usec += shadow_tv_usec;
@@ -242,18 +241,6 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
        struct timeval tv;
        long long time, delta;
        
-#ifdef XENO_TIME_DEBUG
-       static u32 foo_count = 0;
-       foo_count++;            
-       if (foo_count>= 1000) {
-               s64 n = NOW();
-               struct timeval tv;
-               do_gettimeofday(&tv);
-               printk("0x%08X%08X %ld:%ld\n",
-                          (u32)(n>>32), (u32)n, tv.tv_sec, tv.tv_usec);
-               foo_count = 0;
-       }
-#endif
     /*
      * The next bit really sucks:
      * Linux not only uses do_gettimeofday() to keep a notion of
@@ -268,7 +255,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
      * updates xtime accordingly. Yuck!
      */
 
-       /* work out the number of jiffies past and update them */
+       /* Work out the number of jiffy intervals passed and update them. */
        do_gettimeofday(&tv);
        time = (((long long)tv.tv_sec) * 1000000) + tv.tv_usec;
        delta = time - last_irq;
@@ -307,24 +294,14 @@ static struct irqaction irq_timer = {
 void __init time_init(void)
 {
     unsigned long long alarm;
-       u64     cpu_freq = HYPERVISOR_shared_info->cpu_freq;
-       u64 scale;
+    u64 __cpu_khz;
 
-       cpu_khz = (u32)cpu_freq/1000;
+    __cpu_khz = HYPERVISOR_shared_info->cpu_freq;
+    do_div(__cpu_khz, 1000);
+    cpu_khz = (u32)__cpu_khz;
        printk("Xen reported: %lu.%03lu MHz processor.\n", 
                   cpu_khz / 1000, cpu_khz % 1000);
 
-       /*
-     * calculate systemtime scaling factor
-        * XXX RN: have to cast cpu_freq to u32 limits it to 4.29 GHz. 
-        *     Get a better do_div!
-        */
-       scale = 1000000000LL << 32;
-       do_div(scale,(u32)cpu_freq);
-       st_scale_f = scale & 0xffffffff;
-       st_scale_i = scale >> 32;
-       printk("System Time scale: %X %X\n",st_scale_i, st_scale_f);
-
        do_gettimeofday(&xtime);
        last_irq = (((long long)xtime.tv_sec) * 1000000) + xtime.tv_usec;